package org.rcgwt.client.ui.calendar.renderer.impl;

import java.util.Date;

import org.rcgwt.client.ui.calendar.controller.CalendarAction;
import org.rcgwt.client.ui.calendar.controller.CalendarChangesMask;
import org.rcgwt.client.ui.calendar.controller.CalendarController;
import org.rcgwt.client.ui.calendar.event.ModelChangedEvent;
import org.rcgwt.client.ui.calendar.event.ModelChangedHandler;
import org.rcgwt.client.ui.calendar.i18n.CalendarUtils;
import org.rcgwt.client.ui.calendar.model.impl.CalendarModel;
import org.rcgwt.client.ui.calendar.model.impl.SelectionType;
import org.rcgwt.client.ui.calendar.mvc.ICalendarContext;
import org.rcgwt.client.ui.calendar.renderer.IHeaderRenderer;
import org.rcgwt.client.ui.themes.ITheme;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.Cell;

/**
 * A short year & month cursor
 * 
 * @author Elian ORIOU
 * 
 */

@SuppressWarnings("deprecation")
public class ShortCalendarHeaderRenderer implements IHeaderRenderer {

	/* Themes */
	private ITheme theme;

	/* Panels */
	private FlexTable b_table;

	/* Labels */
	private Label l_year;
	private Label l_month;

	/* Year Controls */
	private Label b_previousYear;
	private Label b_nextYear;

	/* Month Controls */
	private Label b_previousMonth;
	private Label b_nextMonth;

	private CalendarController controller;
	private CalendarModel model;

	public ShortCalendarHeaderRenderer() {

	}

	/**
	 * Component rendering method
	 */

	@Override
	public Widget render(ICalendarContext context) {
		theme = context.getTheme();
		controller = context.getController();
		model = context.getModel();

		/* Controls Management */
		b_table = new FlexTable();
		b_table.addStyleName(theme.getYearAndMonthPanelStyle().getClassName());

		/* DOM Structure */
		Node body = b_table.getElement().getLastChild();

		/* Instantiation */

		l_year = new Label(String.valueOf(context.getModel().getCurrentYear()));
		l_year.setStyleName(theme.getYearLabelStyle().getClassName());

		l_month = new Label(context.getModel().getCurrentMonthName());
		l_month.setStyleName(theme.getMonthLabelStyle().getClassName());

		b_previousYear = new Label();
		b_previousYear.setStyleName(theme.getPreviousYearButtonStyle()
				.getClassName());

		b_nextYear = new Label();
		b_nextYear.setStyleName(theme.getNextYearButtonStyle().getClassName());

		b_previousMonth = new Label();
		b_previousMonth.setStyleName(theme.getPreviousMonthButtonStyle()
				.getClassName());

		b_nextMonth = new Label();
		b_nextMonth
				.setStyleName(theme.getNextMonthButtonStyle().getClassName());

		b_table.setWidget(0, 0, b_previousYear);

		Node r1 = body.getFirstChild();
		Element td_year = DOM.createTD();
		td_year.addClassName(theme.getShortColumnStyle().getClassName());
		td_year.appendChild(l_year.getElement());
		r1.appendChild(td_year);

		b_table.setWidget(0, 2, b_nextYear);
		b_table.setWidget(1, 0, b_previousMonth);

		Node r2 = body.getLastChild();
		Element td_month = DOM.createTD();
		td_month.addClassName("shortColumn");
		td_month.appendChild(l_month.getElement());
		r2.appendChild(td_month);

		b_table.setWidget(1, 2, b_nextMonth);

		createButtonHandlers();
		configureHandlers();

		return b_table;
	}

	/**
	 * Creates buttons handlers
	 */

	private void createButtonHandlers() {

		b_previousYear.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				controller.handle(CalendarAction.PREVIOUS_YEAR);
			}
		});

		b_nextYear.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				controller.handle(CalendarAction.NEXT_YEAR);
			}
		});

		b_previousMonth.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
			}
		});

		b_nextMonth.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				controller.handle(CalendarAction.NEXT_MONTH);
			}
		});

		/* Click Event Management */

		b_table.sinkEvents(Event.ONCLICK);
		b_table.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Cell c = b_table.getCellForEvent(event);
				if (c == null) {
					return;
				}
				int row = c.getRowIndex();
				int cell = c.getCellIndex();
				if (row == 0 && cell == 1) {
					controller.handle(CalendarAction.SELECT_YEAR_CURSOR);
					controller.setSelectionType(SelectionType.YEAR);
				} else if (row == 1 && cell == 1) {
					controller.handle(CalendarAction.SELECT_MONTH_CURSOR);
					controller.setSelectionType(SelectionType.MONTH);
				}
			}
		});
	}

	/**
	 * Creates and manages handlers
	 */

	private void configureHandlers() {

		controller.addModelChangedHandler(new ModelChangedHandler() {
			@Override
			public void onModelChangedEvent(ModelChangedEvent event) {
				Date d = event.getDate();
				SelectionType selectionType = model.getSelectionType();
				int type = event.getElementType();

				b_previousMonth.setStyleName(theme
						.getPreviousMonthButtonStyle().getClassName());
				b_nextMonth.setStyleName(theme.getNextMonthButtonStyle()
						.getClassName());
				b_previousYear.setStyleName(theme.getPreviousYearButtonStyle()
						.getClassName());
				b_nextYear.setStyleName(theme.getNextYearButtonStyle()
						.getClassName());

				/* Selection / Deselection (It doesn't require date) */
				if ((type & CalendarChangesMask.SELECT_MONTH) > 0) {
					if (isSelected(l_year) == true) {
						deselectYear();
					}
					selectMonth();
				}
				if ((type & CalendarChangesMask.UNSELECT_MONTH) > 0) {
					deselectMonth();
				}
				if ((type & CalendarChangesMask.SELECT_YEAR) > 0) {
					if (isSelected(l_month) == true) {
						deselectMonth();
					}
					selectYear();
				}
				if ((type & CalendarChangesMask.UNSELECT_YEAR) > 0) {
					deselectYear();
				}
				if ((type & CalendarChangesMask.PREVIOUS_CONTEXT) > 0) {
					if (selectionType == SelectionType.MONTH) {
						l_month.setText(String.valueOf(model
								.getDisplayedMonth()));
					} else if (selectionType == SelectionType.YEAR) {
						l_year
								.setText(String.valueOf(model
										.getDisplayedYear()));
					}
				}
				if ((type & CalendarChangesMask.NEXT_CONTEXT) > 0) {
					if (selectionType == SelectionType.MONTH) {
						l_month.setText(String.valueOf(model
								.getDisplayedMonth()));
					} else if (selectionType == SelectionType.YEAR) {
						l_year
								.setText(String.valueOf(model
										.getDisplayedYear()));
					}
				}
				if ((type & CalendarChangesMask.MIN_OVERFLOW) > 0) {
					b_previousMonth.setStyleName(theme
							.getDisabledPreviousButtonStyle().getClassName());
					b_previousYear.setStyleName(theme
							.getDisabledPreviousButtonStyle().getClassName());
				}
				if ((type & CalendarChangesMask.MAX_OVERFLOW) > 0) {
					b_nextMonth.setStyleName(theme.getDisabledNextButtonStyle()
							.getClassName());
					b_nextYear.setStyleName(theme.getDisabledNextButtonStyle()
							.getClassName());
				}

				if (d == null) {
					return;
				}
				int year = d.getYear() + 1900;
				String month = CalendarUtils.getMonths()[d.getMonth()];

				/* HEADER ACTIONS */
				if ((type & CalendarChangesMask.SPECIFIED_YEAR) > 0) {
					l_year.setText(String.valueOf(year));
				}
				if ((type & CalendarChangesMask.SPECIFIED_MONTH) > 0) {
					l_month.setText(month);
				}
				if ((type & (CalendarChangesMask.SPECIFIED_YEAR | CalendarChangesMask.SPECIFIED_MONTH)) > 0) {
					l_month.setText(month);
					l_year.setText(String.valueOf(year));
				}
				/* FOOTER ACTION */
				if ((type & CalendarChangesMask.CURRENT_DAY) > 0) {
					l_month.setText(month);
					l_year.setText(String.valueOf(year));
				}
			}
		});
	}

	/**
	 * Selects the Year label (for the semantic navigation)
	 */

	public void selectYear() {
		if (isSelected(l_year) == false) {
			l_year.addStyleName(theme.getSelectedYearMonthLabelStyle()
					.getClassName());
		} else {
			deselectYear();
		}
	}

	/**
	 * Deselects the Year label (for the semantic navigation)
	 */

	public void deselectYear() {
		l_year.removeStyleName(theme.getSelectedYearMonthLabelStyle()
				.getClassName());
	}

	/**
	 * Selects the Month label (for the semantic navigation)
	 */

	public void selectMonth() {
		if (isSelected(l_month) == false) {
			l_month.addStyleName(theme.getSelectedYearMonthLabelStyle()
					.getClassName());
		} else {
			deselectMonth();
		}
	}

	/**
	 * Deselects the Month label (for the semantic navigation)
	 */

	public void deselectMonth() {
		l_month.removeStyleName(theme.getSelectedYearMonthLabelStyle()
				.getClassName());
	}

	/**
	 * Returns true if the label is selected (in the context of the component)
	 * 
	 * @param l
	 * @return
	 */

	public boolean isSelected(Label l) {
		return l.getStyleName().contains(
				theme.getSelectedYearMonthLabelStyle().getClassName());
	}

	@Override
	public void update(ICalendarContext context) {
	}

	@Override
	public void onKeyDown(KeyDownEvent event) {
		SelectionType type = model.getSelectionType();
		if (type != SelectionType.MONTH && type != SelectionType.YEAR) {
			return;
		}
		int keyCode = event.getNativeKeyCode();
		switch (keyCode) {
		case KeyCodes.KEY_LEFT:
			if (type == SelectionType.MONTH) {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
			} else if (type == SelectionType.YEAR) {
				controller.handle(CalendarAction.PREVIOUS_YEAR);
			}
			break;
		case KeyCodes.KEY_RIGHT:
			if (type == SelectionType.MONTH) {
				controller.handle(CalendarAction.NEXT_MONTH);
			} else if (type == SelectionType.YEAR) {
				controller.handle(CalendarAction.NEXT_YEAR);
			}
			break;
		case KeyCodes.KEY_HOME:
			if (type == SelectionType.MONTH) {
				if (model.getDisplayedMonth() == 0) {
					controller.handle(CalendarAction.PREVIOUS_YEAR);
				}
				controller.setDisplayedMonth(0);
				controller.handle(CalendarAction.REFRESH_CALENDAR);
			}
			break;
		case KeyCodes.KEY_END:
			if (type == SelectionType.MONTH) {
				if (model.getDisplayedMonth() == 11) {
					controller.handle(CalendarAction.NEXT_YEAR);
				}
				controller.setDisplayedMonth(11);
				controller.handle(CalendarAction.REFRESH_CALENDAR);
			}
			break;
		}
	}

	@Override
	public void onKeyUp(KeyUpEvent event) {
	}

	@Override
	public void onMouseWheel(MouseWheelEvent event) {
		SelectionType type = model.getSelectionType();
		if (type != SelectionType.MONTH && type != SelectionType.YEAR) {
			return;
		}
		event.preventDefault();
		if (type == SelectionType.MONTH) {
			if (event.isNorth() == true) {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
			} else if (event.isSouth() == true) {
				controller.handle(CalendarAction.NEXT_MONTH);
			}
		} else {
			if (event.isNorth() == true) {
				controller.handle(CalendarAction.PREVIOUS_YEAR);
			} else if (event.isSouth() == true) {
				controller.handle(CalendarAction.NEXT_YEAR);
			}
		}
	}
}